package ai.leinao.alarm.calculate;

import ai.leinao.alarm.domain.AlarmRule;
import ai.leinao.alarm.domain.RawAlarm;
import ai.leinao.alarm.limit.AlarmLimitProcessor;
import ai.leinao.alarm.service.AlarmRuleService;
import ai.leinao.common.domain.GatherData;
import ai.leinao.common.queue.MessageQueue;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.util.StrUtil;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 策略1：实时告警计算
 *
 * @author zhong.wang
 * @date 2025/9/8
 **/
@Component
@Slf4j
public class RealTimeAlarmCalculator {
    /**
     * 计算线程数
     */
    private static final int CALCULATE_THREADS = 3;

    private MessageQueue dataQueue;

    private AlarmLimitProcessor alarmLimitProcessor;

    private AlarmRuleService alarmRuleService;

    private ThreadPoolExecutor workerExecutor;

    /**
     * 构造函数
     * @param dataQueue
     * @param alarmLimitProcessor
     */
    public RealTimeAlarmCalculator(MessageQueue dataQueue,
                                   AlarmLimitProcessor alarmLimitProcessor,
                                   AlarmRuleService alarmRuleService){
        this.dataQueue = dataQueue;
        this.alarmLimitProcessor = alarmLimitProcessor;
        this.alarmRuleService = alarmRuleService;
        this.initWorkerExecutor();
        startCalculate();
    }

    /**
     * 初始化线程池
     */
    private void initWorkerExecutor(){
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setUncaughtExceptionHandler((thread, throwable) -> {
                    log.error("alarm workerExecutor has uncaughtException.");
                    log.error(throwable.getMessage(), throwable);
                })
                .setDaemon(true)
                .setNamePrefix("alarm-worker-%d")
                .build();
        workerExecutor = new ThreadPoolExecutor(10,
                10,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                threadFactory,
                new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * 启动计算
     */
    private void startCalculate() {
        Runnable runnable = () -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    GatherData gatherData = dataQueue.pollGatherDataToCalculate();
                    calculate(gatherData);
                } catch (InterruptedException ignored) {
                    Thread.currentThread().interrupt();
                } catch (Exception e) {
                    log.error("计算告警时发生错误: {}.", e.getMessage(), e);
                }
            }
        };
        for (int i = 0; i < CALCULATE_THREADS; i++) {
            workerExecutor.execute(runnable);
        }
    }

    /**
     * 计算
     * @param gatherData
     */
    private void calculate(GatherData gatherData) {
        if (gatherData == null) {
            return;
        }
        List<AlarmRule> alarmRules = alarmRuleService.getAlarmRules();
        if(CollUtil.isEmpty(alarmRules)){
            return;
        }
        Map<String,Object> fieldValueMap = gatherData.toContext();
        for (AlarmRule alarmRule : alarmRules) {
            final String expr = alarmRule.getExpression();
            if (StrUtil.isBlank(expr)) {
                continue;
            }
            // 执行告警表达式，匹配成功则触发告警
            try {
                boolean match = execAlarmExpression(fieldValueMap, expr, true);
                try {
                    if (match) {
                        RawAlarm alarm = new RawAlarm();
                        alarm.setAlarmLevel(alarmRule.getAlarmLevel());
                        alarm.setAlarmContent(alarmRule.getName());
                        alarm.setAlarmStartTime(new java.util.Date());
                        alarm.setAlarmTime(new java.util.Date());
                        alarm.setDeviceId(gatherData.getDeviceId());
                        alarm.setDeviceTypeCode(gatherData.getDeviceTypeCode());
                        alarm.setStationId(gatherData.getStationId());
                        alarm.setStationAreaId(gatherData.getAreaId());
                        alarm.setMainType("data-alarm");
                        alarm.setSubType(alarmRule.getType());
                        alarm.setDerivedProperties(fieldValueMap);
                        alarmLimitProcessor.reduceAndSendAlarm(alarm);
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            } catch (Exception ignored) {}
        }

    }

    /**
     * 执行告警表达式
     * @param fieldValueMap
     * @param expr
     * @param ignoreJexlException
     * @return
     */
    private boolean execAlarmExpression(Map<String, Object> fieldValueMap, String expr, boolean ignoreJexlException){
        Boolean match;
        Expression expression;
        try {
            expression = AviatorEvaluator.compile(expr);
        } catch (Exception e) {
            log.error("告警规则: {} 未知错误: {}.", expr, e.getMessage());
            throw e;
        }

        try {
            match = (Boolean) expression.execute(fieldValueMap);
        } catch (Exception e) {
            log.error("告警规则: {} 未知错误: {}.", expr, e.getMessage());
            throw e;
        }
        return match != null && match;
    }

}
